详解 TypeScript 中的 typeof 和 keyof

您所在的位置:网站首页 instanceofjsjs typeof 详解 TypeScript 中的 typeof 和 keyof

详解 TypeScript 中的 typeof 和 keyof

2023-06-28 01:40| 来源: 网络整理| 查看: 265

typeof和keyof是 TypeScript 中常用的两个操作符,本文会对这两个操作符的基本概念和实际应用做一个详细的阐述。

字面量类型

在了解typeof和keyof之前,我们首先需要了解什么是字面量类型以及联合字面量类型。

TypeScript 中的字面量类型是更具体的string、number或boolean类型,它可以被这样定义:

type Greeting = 'hello';

这意味着类型为Greeting的变量只能有一个字符串值'hello':

const foo: Greeting = 'hello'; const bar: Greeting = 'world'; // Type '"world"' is not assignable to type '"hello"'.ts(2322)

字面量类型本身可能并不是很实用,但是它可以和联合类型一起组合出强大的抽象,也就是我们说的联合字面量类型:

type Greeting = 'hello' | 'world'; const foo: Greeting = 'hello'; const bar: Greeting = 'world'; typeof

typeof操作符用于获取变量的类型,因此操作符后面接的始终是一个变量。

基本用法

假如我们在定义类型之前已经有了对象obj,就可以用typeof来定义一个类型。

const p = { name: 'CJ', age: 18 }; type Person = typeof p; // 等同于 type Person = { name: string; age: number; } 从嵌套对象获取类型

如果对象是一个嵌套的对象,typeof也能够正确获取到它们的类型。

const p = { name: 'CJ', age: 18, address: { city: 'SH' } }; type Person = typeof p; // 相当于 type Person = { name: string; age: number; address: { city: string; }; }; 从数组获取类型

假如我们有一个字符串数组,可以把数组的所有元素组合成一个新的类型:

const data = ['hello', 'world'] as const; type Greeting = typeof data[number]; // type Greeting = "hello" | "world"

as const 是 TypeScript 3.4 中新增的一个特性,具体的可以看这里

甚至我们可以从对象数组中获取我们想要的类型:

export const locales = [ { locale: 'se', language: 'Swedish' }, { locale: 'en', language: 'English' } ] as const; type Locale = typeof locales[number]['locale']; // type Locale = "se" | "en" keyof

keyof操作符后面接一个类型,生成由string或者number组成的联合字面量类型。

基本用法

一个最基本的keyof用法如下,我们通过keyof Person得到一个PersonKeys类型,它是一个联合字面量类型,包含了Person所有的属性。所以我们在对类型为PersonKeys的变量赋值时,只能赋值为'name'或者'age'。

type Person = { name: string; age: number; }; type PersonKeys = keyof Person; const key1: PersonKeys = 'name'; const key2: PersonKeys = 'age'; // Type '"addr"' is not assignable to type 'keyof Person'. const key3: PersonKeys = 'addr'; 与泛型一起使用

我们希望获取一个对象给定属性名的值,为此,我们需要确保我们不会获取 obj 上不存在的属性。所以我们在两个类型之间建立一个约束:

export const getProperty = (obj: T, key: K) => { return obj[key]; }; const person = { name: 'CJ', age: 18 }; console.log(getProperty(person, 'name')); // Argument of type '"addr"' is not assignable to parameter of type '"name" | "age"'. console.log(getProperty(person, 'addr'));

keyof T返回T的联合字面量类型,extends用来对K进行约束,表示K为联合字面量类型中的一个。

由于我们使用了类型约束,这样我们在调用getProperty的时候,第二个参数key就必须为第一个参数obj中的属性。在尝试传入不存在的addr属性时 TypeScript 就会报错。

与映射类型一起使用

keyof运算符的另一个常见用途是映射类型,通过遍历键将现有类型转换为新类型。

下面是如何使用OptionsFlags映射类型转换FeatureFlags类型的示例。

type OptionsFlags = { [Property in keyof T]: boolean; }; // use the OptionsFlags type FeatureFlags = { darkMode: () => void; newUserProfile: () => void; }; type FeatureOptions = OptionsFlags; // 相当于 // type FeatureOptions = { // darkMode: boolean; // newUserProfile: boolean; // };

在这个例子中,OptionFlags被定义为类型参数为T的一个泛型,[Property in keyof T]表示T所有属性名的迭代,方括号是索引签名语法。所以,OptionFlags包含T类型的所有属性,并将它们的值重新映射为boolean型。

与条件映射类型一起使用

在上一个例子中,我们把所有属性都映射成了boolean型。我们还可以更进一步,使用条件类型来进行类型映射。

在下面的例子中,我们只映射非函数属性为boolean型。

type OptionsFlags = { [Property in keyof T]: T[Property] extends Function ? T[Property] : boolean; }; type Features = { darkMode: () => void; newUserProfile: () => void; userManagement: string; resetPassword: string; }; type FeatureOptions = OptionsFlags; // 相当于 // type FeatureOptions = { // darkMode: () => void; // newUserProfile: () => void; // userManagement: boolean; // resetPassword: boolean; // }; 与 utility types 一起使用

TypeScript 内置了一些映射类型,叫做utility types。Record就是其中之一,为了理解Record类型如何工作,我们来看一下它的定义:

type Record = { [P in K]: T; };

可以看到,Record只是将所有属性映射为T类型之后返回的一个新类型。所以我们可以很容易通过Record实现上面映射类型中的例子。

type FeatureOptions = Record;

另外一个常见的用到keyof的类型是Pick。它允许从一个对象类型中选择一个或多个属性,并创建一个新类型。

type Pick = { [P in K]: T[P]; }; 总结

本篇文章到这里就结束了,主要介绍了typeof和keyof的一些常见的用法。在 TypeScript 的类型系统中,如果我们恰当地使用这两个操作符,可以帮助我们构造简洁并且受约束的类型,来提高我们代码的类型安全性。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3